//
//  BottomView.m
//  TraceViewer
//
//  Created by Vladimir Sarpe on 24/06/08.
//  Copyright 2008 __MyCompanyName__. All rights reserved.
//

#import "BottomView.h"


@implementation BottomView

- (id)initWithFrame:(NSRect)frame {
    self = [super initWithFrame:frame];
    if (self) {
		frameRect = [self bounds];
		location = NSMakePoint(30.0,1.0);
		dragging = NO;
		staticWidth = frameRect.size.width / 50;
		selectorWidth = staticWidth;
		selectorHeight = 62;
		zoom = 1.0;
		initdraw = NO;
		lastevent = 0;
		prevRect = frameRect;
		prevPos = location.x/frameRect.size.width;
    }
    return self;
}

- (void)drawRect:(NSRect)rect {
	NSBezierPath *sel;
	NSBezierPath *sel_b;
	frameRect = [self bounds];

	if ((initdraw == NO) && (lastevent > 0)) {
		[self drawGraphs:graphSends andSpawns:graphSpawns andDies:graphDies];
		initdraw = YES;
	}
	int timeline_l = lastevent * (location.x/frameRect.size.width);
	int timeline_r = lastevent * ((location.x+selectorWidth) / frameRect.size.width);
	int x = (logf(timeline_r))/(logf(10)); // log base 10
	int x_pos = 60 + (x*6);
	NSString* timeloc;
	if ([self inLiveResize]) {
		timeloc = @"RESIZING...";
	}
	else {
		timeloc = [NSString stringWithFormat:@"Range: %d µs\n\t   %d µs", timeline_l, timeline_r];
		if (prevRect.size.width != frameRect.size.width) {
			k = prevPos;
			[self viewDidEndLiveResize];
		}
	}
	[self validateSelector];
	prevPos = location.x/frameRect.size.width;
	prevRect = frameRect;
	
	// draw background
	[[NSColor colorWithCalibratedWhite:0.8 alpha:1.0] set];
	NSRectFill(rect);
	NSBezierPath *borderPath= [[NSBezierPath alloc] init];
    [borderPath moveToPoint:NSMakePoint(0, 0)];
	[borderPath lineToPoint:NSMakePoint(rect.size.width, 0)];
    [borderPath moveToPoint:NSMakePoint(0, rect.size.height)];
    [borderPath lineToPoint:NSMakePoint(rect.size.width, rect.size.height)];
    [borderPath closePath];
	[[NSColor blackColor] set];
    [borderPath stroke];
	
	// draw selector
	selector = NSMakeRect(location.x,location.y,selectorWidth,selectorHeight);
	selector_border = NSMakeRect(location.x,location.y,selectorWidth,selectorHeight);
	sel = [NSBezierPath bezierPathWithRect:selector];
	[[NSColor whiteColor] set];
	[sel fill]; 
	sel_b = [NSBezierPath bezierPathWithRect:selector_border];
	[[NSColor blackColor] set];
	[sel_b stroke];
	
	// draw graph
	[[NSColor colorWithCalibratedRed:0.0 green:0.6 blue:0.0 alpha:1.0] set];
	[sendp stroke];
	[[NSColor redColor] set];
	[diep stroke];
	[[NSColor blueColor] set];
	[spawnp stroke];
	
	// draw string
	NSMutableAttributedString *s = [[NSMutableAttributedString alloc] initWithString:timeloc];
	[s addAttribute:NSFontAttributeName	
			  value:[NSFont userFontOfSize:10]
			  range:NSMakeRange(0,[s length])];
	[s addAttribute:NSForegroundColorAttributeName
			  value:[NSColor blackColor]
			  range:NSMakeRange(0,[s length])];
	NSRect text = NSMakeRect(frameRect.size.width-x_pos, frameRect.size.height-40, (50 + x_pos), 40);
	[s drawInRect:text];
}

-(void) viewWillStartLiveResize {
	k = location.x/frameRect.size.width; // uncomment for static selector (2/2)
}

-(void) viewDidEndLiveResize {
	graphSends = [self graphStats:rawSends];
	graphSpawns = [self graphStats:rawSpawns];
	graphDies = [self graphStats:rawDies];
	[self drawGraphs:graphSends andSpawns:graphSpawns andDies:graphDies];
	location.x = k * (frameRect.size.width); // uncomment for static selector (1/2)
	[self setNeedsDisplay:YES];
	[topView setNeedsDisplay:YES];
}

-(void) drawGraphs:(NSMutableArray*) sends andSpawns:(NSMutableArray*) spawns andDies:(NSMutableArray*) dies {
	sendp = [self drawPath:sends];
	diep = [self drawPath:dies];
	spawnp = [self drawPath:spawns];
}

-(NSBezierPath*) drawPath:(NSMutableArray*) data {
	NSBezierPath* path;
	int i; 
	int nextx = 0;
	int nexty = 10;
	if ([data count] > 0){
		path = [[NSBezierPath alloc] init];
		[path setLineWidth:1];
		[path moveToPoint:NSMakePoint(nextx, nexty)];
		for (i = 0; i < [data count]; i++) {
			float getx = ((([[[data objectAtIndex:i] objectAtIndex:1] floatValue]) / lastevent) * frameRect.size.width);
			int prevx = nextx;
			nextx = getx;
			if (nextx == 0){
				nextx = prevx+1;
			}
			float value = [[[data objectAtIndex:i] objectAtIndex:0] floatValue];
			float logv;
			if (value == 0.0){
				logv = 0.0;
			}
			else {
				logv = logf(value);

			}
			float logm = logf(max);
			nexty = 10 + ((logv/logm)*(frameRect.size.height-12));
			[path lineToPoint:NSMakePoint(nextx, nexty)];
		}
		[path lineToPoint:NSMakePoint(frameRect.size.width, 10)];
	}
	[self setNeedsDisplay:YES];
	return path;
}

- (void) validateSelector {
	frameRect = [self bounds];
	staticWidth = frameRect.size.width / 50;
	selectorWidth = staticWidth/zoom;
	[self newLocationByX:0.0];
}

-(BOOL)acceptsFirstResponder{
	return YES;
}

-(BOOL)isOpaque { // so we dont move the window on click
    return [[NSColor whiteColor] alphaComponent] >= 1.0 ? YES : NO;
}

-(void)mouseDown:(NSEvent *)event {
	NSPoint clickLocation;
	BOOL itemHit = NO;	
	clickLocation = [self convertPoint:[event locationInWindow]
							  fromView:nil];
	itemHit = [self isPointInItem:clickLocation];
	if (itemHit) {
		dragging = YES;
	}
	else {
		if (((location.x > clickLocation.x) && ((location.x + selectorWidth/2 - clickLocation.x) < 20)) || ((location.x < clickLocation.x) && (location.x + selectorWidth/2 - clickLocation.x) > -30)) {
			dragging = YES; // this is so if the selector window is too small, the user can still hit it if it is approximately close by
		}
		else {
			[self newLocationByX:(clickLocation.x - location.x - selectorWidth/2)];
			dragging = YES;
			[self setNeedsDisplay:YES];
		}
	}
	lastDragLocation = clickLocation;
	[[NSCursor closedHandCursor]push];
} 
 
-(BOOL)isPointInItem:(NSPoint)testPoint {
    BOOL itemHit=NO;
    itemHit = NSPointInRect(testPoint,selector);
    /*if (itemHit) {
    }*/
    return itemHit;
}

-(void)mouseDragged:(NSEvent *)event {
    if (dragging) {
		[[NSCursor closedHandCursor]push];
		NSPoint newDragLocation=[self convertPoint:[event locationInWindow]
										  fromView:nil];
		[self newLocationByX:(newDragLocation.x-lastDragLocation.x)];
		lastDragLocation=newDragLocation;
		[self autoscroll:event];
    }
}

- (void)newLocationByX:(float)x {
    location.x=location.x+x;
	if (location.x < 0) {
		location.x = 0;
	}
	if (location.x > (frameRect.size.width - selectorWidth)) {
		location.x = (frameRect.size.width - selectorWidth);
	}
	[self setNeedsDisplay:YES];
}

-(void)mouseUp:(NSEvent *)event {
    dragging=NO;
    [NSCursor pop];
	int timeline_l = lastevent * (location.x/frameRect.size.width);
	int timeline_r = lastevent * ((location.x+selectorWidth) / frameRect.size.width);
	[topView notifyChangePositionLeft:timeline_l andRight:timeline_r];
    [[self window] invalidateCursorRectsForView:self];
	[self setNeedsDisplay:YES];
}

-(void)resetCursorRects {
    [self discardCursorRects];
    NSRect clippedItemBounds = NSIntersectionRect([self visibleRect], selector);
    if (!NSIsEmptyRect(clippedItemBounds)) {
		[self addCursorRect:clippedItemBounds cursor:[NSCursor openHandCursor]];
    }
}

-(void)notifyZoom:(float)z {
	zoom = z;
	selectorWidth = staticWidth/zoom;
	[self newLocationByX:0.0];
	//[self setNeedsDisplay:YES];
}

-(NSMutableArray*)graphStats:(NSMutableArray*) data {
	NSMutableArray* vals = [[NSMutableArray alloc] init];
	int i;
	int lastev = (frameRect.size.width);
	int numev = (lastevent) / (lastev-1);
	for (i = 0; i < lastev; i++) {
		NSMutableArray* vals_element = [NSMutableArray arrayWithCapacity:2];
		[vals_element addObject:[NSNumber numberWithInt:0]];
		[vals_element addObject:@"-1"];
		[vals addObject:vals_element];
	}
	for (i = 0; i < [data count]; i++) {
		NSString* event = [data objectAtIndex:i];
		int event_pos = [event intValue] / numev;
		NSString* event_avg_ts = [[vals objectAtIndex:event_pos] objectAtIndex:1];
		int avg;
		if ([event_avg_ts isEqualToString:@"-1"]) {
			avg = [event intValue];
		}
		else {
			avg = ([event intValue] + [event_avg_ts intValue]) / 2;
		}
		NSString* avg_str = [NSString stringWithFormat:@"%d", avg]; // we place the pixel.. at the AVERAGE timestamp of all the stacked values
		[[vals objectAtIndex:event_pos] replaceObjectAtIndex:1 withObject:avg_str];
		int update = [[[vals objectAtIndex:event_pos] objectAtIndex:0] intValue] + 1;
		[[vals objectAtIndex:event_pos] replaceObjectAtIndex:0 withObject:[NSNumber numberWithInt:update]];
		if (update > max) {
			max = update;
		}
	}
	return vals;
}

-(void)loadSends:(NSMutableArray *)send andSpawn:(NSMutableArray *)spawn andDie:(NSMutableArray *)die andLast:(NSString*)last {
	initdraw = NO;
	scale = 1;
	max = 0;
	rawSends = send;
	rawSpawns = spawn;
	rawDies = die;
	graphSends = [[NSMutableArray alloc] init];
	graphSpawns = [[NSMutableArray alloc] init];
	graphDies = [[NSMutableArray alloc] init];
	lastevent = [last intValue];
	graphSends = [self graphStats:send];
	graphSpawns = [self graphStats:spawn];
	graphDies = [self graphStats:die];
	[topView test]; // test resizze of top view.
	[self setNeedsDisplay:YES];
}
@end
